//  Copyright (c) 2009 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using DNA.Mvc.Controllers;
using DNA.Mvc.jQuery;
using DNA.Mvc.Publishing;
using DNA.OpenAPI.Atom;
using DNA.Utility;
using DNA.OpenAPI.Rss;
using DNA.Mvc.Notification;
using System.Web.Security;
using DNA.Mvc.Management;

namespace DNA.Mvc.Publishing.Controllers
{
    //[Log]
    public partial class ArticleController : Controller
    {
        private IPubService srv;
        //private WebSiteContext websiteContext;,WebSiteContext webContext

        public ArticleController(IPubService service)
        {
            // websiteContext = webContext;
            srv = service;
        }

        #region internal wrappers

        private Article _Find(int id)
        {
            if (id == 0)
                throw new ArgumentOutOfRangeException("id");

            var result = srv.GetPost(id);

            if (result == null)
                throw new HttpException(404, "Post not found.");

            return result;
        }

        private Category _Find(string path)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            var cat = srv.FindCategory(path);

            if (cat == null)
                throw new HttpException(404, "Category not found");

            return cat;
        }

        private ArticleWrapper _Wrap(int id) { return new ArticleWrapper(_Find(id)); }

        private IEnumerable<Article> _GetRecents(int rows)
        {
            var website = _GetWebSiteName();
            var total = 0;
            return srv.GetRecentPosts(website, 0, rows, out total);
        }

        private string _GetWebSiteName()
        {
            IDictionary<string, object> values = RouteData.Values;
            var website = "home";
            if (values.ContainsKey("website") && !string.IsNullOrEmpty(values["website"] as string))
                website = values["website"].ToString();
            return website;
        }

        private IEnumerable<Article> _GetDrafts()
        {
            var website = _GetWebSiteName();
            return srv.GetDrafts(website);
        }

        private Category _GetDefault(IDictionary<string, object> values)
        {
            return srv.FindCategory(_GetWebSiteName());
        }

        private TranslatedCopy _Update(TranslatedCopy copy)
        {
            return srv.Translate(copy);
        }

        private Comment _Update(Comment comment)
        {
            return srv.AddComment(comment);
        }

        private void _Delete(int id)
        {
            if (id <= 0)
                throw new ArgumentOutOfRangeException("id");
            srv.DeletePost(id);
        }

        #endregion

        [SiteMapAction(Title = "Publishing")]
        public ActionResult Index()
        {
            int totalRecords = 0;
            ViewData.Model = _GetRecents(20);
            ViewData["TotalRecords"] = totalRecords;
            return View();
        }

        [OutputCache(Duration = 3600, VaryByParam = "*")]
        public ActionResult Rss(int id)
        {
            var article = _Wrap(id);
            var doc = new RssDocument();
            var channel = new RssChannel()
            {
                Title = article.Title,
                Description = article.Summary,
                Generator = "DotNetAge " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(),
                Link = article.PermaLink,
                Language = article.Language,
                LastBuildDate = article.LastModified.ToString("r"),
                PubDate = article.Posted.ToString("r"),
                DocUrl = article.PermaLink
            };

            var comments = article.Comments.Where(c => c.IsApproved);
            channel.Items = new List<RssItem>();
            foreach (var c in comments)
            {
                channel.Items.Add(new RssItem()
                {
                    Title = c.Title,
                    Author = c.UserName,
                    Description = c.Body,
                    Link = article.PermaLink,
                    PubDate = c.Posted.ToString("r")
                });
            }
            doc.Channel = channel;

            string xml = XmlSerializerUtility.SerializeToXml<RssChannel>(channel);
            xml = xml.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "<rss version=\"2.0\">");
            return Content(xml, "text/xml");
        }

        [OutputCache(Duration = 3600, VaryByParam = "*")]
        public ActionResult Atom(int id)
        {
            var article = _Wrap(id);

            var feedBuilder = new AtomFeedBuilder(article.Title, id: article.PermaLink);
            feedBuilder.SetLastUpdated(article.LastModified);
            feedBuilder.SetGenerator("http://www.dotnetage.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), "DotNetAge");

            feedBuilder.AddEntries(article.Comments.Where(c => c.IsApproved), (c, b) =>
            {
                b.SetAuthor(c.UserName, c.WebSite, c.Email);
                b.SetContent(c.Body, src: article.PermaLink);
                b.SetBaseInfo(article.PermaLink + "#" + c.ID.ToString(), c.Title, c.Posted);
            });

            var feed = feedBuilder.GetAtomFeed();
            string xml = XmlSerializerUtility.SerializeToXml<AtomFeed>(feed);
            return Content(xml, "text/xml");
        }

        [SiteMapAction(
            Title = "Articles",
            PersonalTitle = "Blog",
            IsShared = true,
            ShowInMenu = false,
            IgnoreRouteDataKeys = new string[] { "path", "index", "size" })]
        [Pagable]
        public ActionResult List(string path, QueryParams query)
        {
            if (Request.QueryString.AllKeys.Contains("index"))
            {
                var _index = Request.QueryString["index"];
                if (string.IsNullOrEmpty(_index))
                    return HttpNotFound();
            }

            Category category = null;
            //Root
            if (string.IsNullOrEmpty(path))
            {
                category = _GetDefault(RouteData.Values);
                ViewData["Title"] = ResMan.Uncategorized;
            }
            else
            {
                string catPath = string.Format("{0}/" + path, _GetWebSiteName());
                category = _Find(catPath);

                if (category == null)
                    return HttpNotFound();

                ViewData["Title"] = category.Title;
            }

            ViewData["Category"] = category;
            var skips = query.GetSkipRecordCount(true);
            var posts = skips > 0 ? category.Articles.OrderByDescending(a => a.Posted).Skip(skips).Take(query.Size) : category.Articles.OrderByDescending(a => a.Posted).Take(query.Size);
            var total = category.Articles.Count();
            return View(new ModelWrapper(posts, total));
        }

        [SiteMapAction(
            Title = "View article",
            PersonalTitle = "View post",
            IsShared = true,
            ShowInMenu = false,
            IgnoreRouteDataKeys = new string[] { "id", "year", "month", "day", "title" }
            )]
        [HttpGet]
        public ActionResult Details(int id)
        {
            var article = _Wrap(id);

            //if ((!article.IsPublished) || (article.IsPrivate))
            //{
            //    if (!article.IsOwner(HttpContext))
            //        return HttpNotFound();
            //    //throw new HttpException(404, "Page not found.");
            //}
            var prev = srv.GetPrevSiblingArticle(article);
            var next = srv.GetNextSiblingArticle(article);
            ViewBag.PrevSibling = prev != null ? new ArticleWrapper(prev) : null;
            ViewBag.NextSibling = next != null ? new ArticleWrapper(next) : null;

            //ViewBag.RelateArticles=srv.get
            ViewData.Model = article;

            if (!string.IsNullOrEmpty(article.RelatedPosts))
            {
                var rids = new List<int>();
                var strIDs = article.RelatedPosts.Split(',');
                foreach (var strID in strIDs)
                {
                    int _id = 0;
                    int.TryParse(strID, out _id);
                    if ((_id > 0) && (!rids.Contains(_id)))
                        rids.Add(_id);
                }
                ViewBag.RelateArticles = srv.GetPosts(rids.ToArray());
            }

            //Internal use
            //WebContext.Current.SetCurrent(article);

            //ViewData["Articles"] = article.Category.Articles; //.GetChildrenArticles();

            if (HttpContext.Request.IsAuthenticated)
                if ((article.UserName.ToLower() != HttpContext.User.Identity.Name.ToLower()))
                    return View();

            //Uri uri = null;
            //Uri.TryCreate(article.PermaLink, UriKind.Absolute, out uri);

            if (article.IsPublished && article.IsAppoved && !article.IsPrivate)
                srv.ReadPost(id, User.Identity.Name, Request.UserHostAddress, Request.UserAgent);

            /*Set seo meta*/
            WebSiteContext.Current.Title = article.Title;

            if (!string.IsNullOrEmpty(article.Summary))
                WebSiteContext.Current.Description=article.Summary;
            else
            {
                    if (!string.IsNullOrEmpty(article.Body))
                    {
                        var clearBody = TextUtility.ClearHtml(article.Body);
                        var bodySummary = clearBody;
                        if (clearBody.Length > 150)
                            bodySummary = clearBody.Substring(0, 150);
                        else
                            bodySummary = clearBody;
                        WebSiteContext.Current.Description = bodySummary;
                    }
            }

            var keywords = new List<string>();
            if (!string.IsNullOrEmpty(article.Tags))
                keywords.AddRange(article.Tags.Split(','));

            if (!string.IsNullOrEmpty(article.Categories))
                keywords.AddRange(article.Categories.Split(','));

            keywords.Add(article.Category.Title);
            
            WebSiteContext.Current.Keywords=string.Join(",", keywords.Distinct().ToArray());
            return View();
        }

        public ActionResult WebLayout()
        {
            return View();
        }

        public ActionResult WebPreview()
        {
            return View();
        }

        [SecurityAction("Publishing", "Move article", "Allows user can change the article position.",
            TitleResName = "SA_MoveArticle",
            DescResName = "SA_MoveArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        public void Move(int id, int pos, int? categoryID)
        {
            //var article = _Find(id);
            //if (article == null)
            //    throw new ArgumentOutOfRangeException("id");
            srv.MovePost(id, pos, (categoryID.HasValue ? categoryID.Value : -1));
        }

        [SecurityAction("Publishing", "Write article", "Allows user go to the write article page.",
            TitleResName = "SA_WriteArticle",
            DescResName = "SA_WriteArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        public ActionResult Create(int? format)
        {
            ViewBag.DefaultCategoryID = srv.FindCategory(_GetWebSiteName());
            ViewBag.Provider = new CategoryNodeProvider(DependencyResolver.Current.GetService<IPubContext>().Categories);

            ViewData["Format"] = format.HasValue ? format.Value : (int)TextFormats.Xhtml;
            return View();
        }

        [HandleError]
        [SecurityAction("Publishing", "Write article", "Allows user go to the write article page.",
            TitleResName = "SA_WriteArticle",
            DescResName = "SA_WriteArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        [HttpPost]
        [ValidateInput(false)]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Article article)
        {
            ModelState.Remove("Article.Posted");

            if (ModelState.IsValid)
            {
                article.UserName = User.Identity.Name;
                article.Language = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
                var isAdmin = false;
                var web = WebSiteContext.Current.Web;

                if (User.IsInRole("administrators") || web.Owner.Equals(User.Identity.Name))
                    isAdmin = true;

                srv.NewPost(article, isAdmin);
                return Redirect(new ArticleWrapper(article).PermaLink);
            }

            ViewData["Format"] = article.ContentFormat;
            ViewBag.Provider = new CategoryNodeProvider(DependencyResolver.Current.GetService<IPubContext>().Categories);
            return View(article);
        }

        [HandleError]
        [SecurityAction("Publishing", "Delete article", "Allows user to delete the article",
            TitleResName = "SA_DeleteArticle",
            DescResName = "SA_DeleteArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        public ActionResult Delete(int id)
        {
            var article = _Find(id);
            
            if (article == null)
                throw new ArgumentOutOfRangeException("Article not found");

            var cat = new CategoryWrapper(article.Category);
            _Delete(id);
            return Redirect(cat.PermaLink);
        }

        [ValidateAntiForgeryToken, HttpPost, ValidateInput(false)]
        public ActionResult AddComment(int id, FormCollection form)
        {
            if (Spammers.IsSpam(HttpContext))
                return View();

            var article = _Find(id);
            if (article == null)
                throw new ArgumentOutOfRangeException(ResMan.ArticleNotFound);

            if ((!HttpContext.Request.IsAuthenticated) && (!article.Category.AllowAnonymousPostComment))
                throw new ArgumentOutOfRangeException();

            var comment = new Comment();

            if (TryUpdateModel<Comment>(comment, "Comment", new string[] { "Title", "UserName", "Body", "Email", "Website" }))
            {
                var testBody = comment.Body;
                if (!string.IsNullOrEmpty(testBody))
                    testBody = TextUtility.ClearHtml(testBody);

                if (string.IsNullOrEmpty(testBody))
                {
                    ViewData.ModelState.AddModelError("", "Please write down something");
                    return View(comment);
                }
                
                if ((article.Category.AllowAnonymousPostComment) && (!HttpContext.Request.IsAuthenticated))
                {
                    bool isExists = Membership.GetUser(comment.UserName) != null;
                    if (isExists)
                    {
                        ViewData.ModelState.AddModelError("UserName", ResMan.UserNameExists);
                        return View(comment);
                    }
                    else
                    {
                        isExists = !string.IsNullOrEmpty(Membership.GetUserNameByEmail(comment.Email));
                        if (isExists)
                        {
                            ViewData.ModelState.AddModelError("Email", ResMan.EmailExists);
                            return View(comment);
                        }
                    }
                }

                comment.Title = "Re:" + article.Title;
                comment.IP = HttpContext.Request.UserHostAddress;
                comment.Article = article;
                comment.UrlReferrer = Request.UrlReferrer != null ? Request.UrlReferrer.ToString() : "";

                _Update(comment);

                new NewCommentEvent(comment).Raise(HttpContext);

                ViewData["Success"] = true;
                return View();
            }

            return View(comment);
        }

        public ActionResult AddComment(int id)
        {
            return View();
        }

        [Authorize, HttpPost]
        public void DeleteComment(int id)
        {
            var comment = srv.FindComment(id);
            var articleID = comment.Article.ID;
            if (comment.Article != null)
            {
                if (User.IsAdministrator() || comment.Article.IsOwner(User.Identity.Name))
                {
                    srv.DeleteComment(id);
                }
            }
            //return RedirectToAction("Detail", new { Area = "publishing", id = articleID });
        }

        [SecurityAction("Publishing", "Mark spammers", "Allows user can mark the comments as spam.",
                        TitleResName = "SA_Markspammer",
            DescResName = "SA_SA_MarkspammerDesc",
            PermssionSetResName = "SA_Publishing"
            ), HttpPost]
        public ActionResult Spam(int id)
        {
            string website = _GetWebSiteName();
            var comment = srv.FindComment(id);
            Spammers.Mark(comment.IP,comment.WebSite);
            return Redirect(new ArticleWrapper(comment.Article).PermaLink);
        }

        public ActionResult Comments(int id)
        {
            ViewData.Model = _Find(id);// Service.GetArticle(id);
            return View();
        }

        [HandleError]
        [SecurityAction("Publishing", "Edit article", "Allows user can edit the article.",
            TitleResName = "SA_EditArticle",
            DescResName = "SA_EditArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        public ActionResult Edit(int id, int? format)
        {
            var article = _Wrap(id);
            ViewBag.DefaultCategoryID = srv.FindCategory(_GetWebSiteName());
            ViewBag.Provider = new CategoryNodeProvider(DependencyResolver.Current.GetService<IPubContext>().Categories);

            if (!article.IsOwner(HttpContext))
                return HttpNotFound();

            ViewData["Format"] = format.HasValue ? format.Value : article.ContentFormat;
            return View(article);
        }

        [HandleError]
        [SecurityAction("Publishing", "Edit article", "Allows user can edit the article.",
            TitleResName = "SA_EditArticle",
            DescResName = "SA_EditArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        [HttpPost]
        [ValidateInput(false)]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id, FormCollection forms)
        {
            var article = _Find(id);
            var wrapper = new ArticleWrapper(article);

            if (!wrapper.IsOwner(HttpContext))
                throw new HttpException(404, "Page not found.");

            if (TryUpdateModel<Article>(article, "Article"))
            {
                article.UserName = User.Identity.Name;
                srv.EditPost(article);
                return Redirect(wrapper.PermaLink);
            }

            ViewBag.DefaultCategoryID = srv.FindCategory(_GetWebSiteName());
            ViewBag.Provider = new CategoryNodeProvider(DependencyResolver.Current.GetService<IPubContext>().Categories);

            return View(article);
        }

        [HandleError]
        [SecurityAction("Publishing", "Translate", "Allows user can go to the translate page.",
            TitleResName = "SA_TranslateArticle",
            DescResName = "SA_TranslateArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        public ActionResult Translate(int id, string lang, int? format)
        {
            var article = _Wrap(id);
            var tcopy = article.GetTranslated(lang);

            if (format.HasValue)
                ViewData["Format"] = format.Value;
            else
            {
                if (tcopy != null && tcopy.ID > 0)
                    ViewData["Format"] = tcopy.ContentFormat;
            }

            if (tcopy != null)
                ViewData.Model = tcopy;

            ViewData["Article"] = article;
            var targetCulture = new System.Globalization.CultureInfo(lang);
            ViewData["TargetCulture"] = targetCulture;
            return View();
        }

        public ActionResult UnlockConent(int id, string password)
        {
            var article = _Find(id);
            if (article.Password.Equals(password))
            {
                return PartialView(article);
            }
            else
                return PartialView();
        }

        [HandleError]
        [SecurityAction("Publishing", "Translate", "Allows user can go to the translate page.",
            TitleResName = "SA_TranslateArticle",
            DescResName = "SA_TranslateArticleDesc",
            PermssionSetResName = "SA_Publishing"
            )]
        [HttpPost]
        [ValidateAntiForgeryToken]
        [ValidateInput(false)]
        public ActionResult Translate(int id, string lang, FormCollection form)
        {
            //var article = _Wrap(id);
            var art = _Find(id);
            var article = new ArticleWrapper(art);

            //if (article == null)
            //throw new ArgumentNullException(Resources.publishing.ArticleNotFound);

            var tcopy = article.GetTranslated(lang);
            if (tcopy == null)
                tcopy = new TranslatedCopy() { ArticleID = id };

            if (TryUpdateModel<TranslatedCopy>(tcopy, "TranslatedCopy", new string[] { "Title", "Body", "Summary", "ContentFormat" }))
            {
                tcopy.Language = lang;
                tcopy.Article = art;
                _Update(tcopy);
                return RedirectToAction("Details", new { id = id });
            }

            return RedirectToAction("Translate", new { id = id, lang = lang });
        }

        [HttpPost]
        public double Vote(int id, int value)
        {
            int total = 0;
            var username = "";
            var ip = Request.UserHostAddress;

            if (Request.IsAuthenticated)
                username = User.Identity.Name;
            else
                username = ip;

            return srv.Vote(id, value, username, ip, out total);
        }

        [Pagable]
        public ActionResult Archive(QueryParams query, int year, int month)
        {
            if (Request.QueryString.AllKeys.Contains("index"))
            {
                var _index = Request.QueryString["index"];
                if (string.IsNullOrEmpty(_index))
                    return HttpNotFound();
            }

            ViewBag.Archive = new Archive()
            {
                Year = year,
                Month = month
            };
            var totalRecords = 0;
            var posts = srv.GetArchivePosts(_GetWebSiteName(), year, month, query.Index - 1, query.Size, out totalRecords);
            return View(new ModelWrapper(posts, totalRecords));
        }

        [Pagable]
        public ActionResult Tags(QueryParams query, string tag)
        {
            if (Request.QueryString.AllKeys.Contains("index"))
            {
                var _index = Request.QueryString["index"];
                if (string.IsNullOrEmpty(_index))
                    return HttpNotFound();
            }

            var totalRecords = 0;
            ViewBag.TagName = tag;
            var posts = srv.GetTagPosts(_GetWebSiteName(), tag, query.Index - 1, query.Size, out totalRecords);
            return View(new ModelWrapper(posts, totalRecords));
        }

        public ActionResult ManageList(int id)
        {
            return View(new CategoryWrapper(srv.FindCategory(id)));
        }

        /// <summary>
        /// Get articles by specified category id (new for DNA2)
        /// </summary>
        /// <param name="id"></param>
        /// <param name="_params"></param>
        /// <returns></returns>
        [Pagable]
        public ActionResult GetArticles(int id, QueryParams _params)
        {
            int total = 0;
            var arts = srv.GetCategoryPosts(id, _params.Index - 1, _params.Size, out total);
            var articles = arts.Select(a => new ArticleWrapper(a)).ToList();
            return View(new ModelWrapper<ArticleWrapper>()
            {
                Total = total,
                Model = articles
            });
        }

        public ActionResult GetDrafts()
        {
            ViewBag.Title = ResMan.Drafts;
            //HttpContext.GetGlobalResourceObject("publishing", "Drafts", System.Globalization.CultureInfo.GetCultureInfo(WebSite.CurrentWeb().GetCurrentLanguage())).ToString();
            ViewData.Model = _GetDrafts();// this.CurrentWeb().GetDrafts();
            return View("~/Areas/Publishing/Views/Article/GetLatestArticles.cshtml");
        }

        public ActionResult GetLatestArticles(int? top = 20)
        {
            ViewBag.Title = ResMan.LatestUpdated;
            //HttpContext.GetGlobalResourceObject("publishing", "LatestUpdated", System.Globalization.CultureInfo.GetCultureInfo(WebSite.CurrentWeb().GetCurrentLanguage())).ToString();
            return View(_GetRecents(top.Value)); // this.CurrentWeb().GetRecentArticles(top.Value)
        }
    }
}
